"""
用户认证路由模块
处理用户注册、登录、登出等认证相关请求
"""
from flask import Blueprint, request, jsonify, current_app, url_for
from models import User, db
from utils.text_auth import AuthManager, token_required, validate_password, validate_email
import os
import uuid
from werkzeug.utils import secure_filename

# 创建认证蓝图
auth_bp = Blueprint('auth', __name__)


@auth_bp.route('/register', methods=['POST'])
def register():
    """
    用户注册接口
    
    请求体参数:
        username (str): 用户名
        email (str): 邮箱地址
        password (str): 密码
        
    返回:
        JSON: 注册结果信息
    """
    try:
        data = request.get_json()
        
        # 验证必需字段
        if not data or not all(k in data for k in ('username', 'email', 'password')):
            return jsonify({'message': '缺少必需字段'}), 400
        
        username = data['username'].strip()
        email = data['email'].strip().lower()
        password = data['password']
        
        # 验证输入数据
        if not username or len(username) < 3 or len(username) > 20:
            return jsonify({'message': '用户名长度必须在3-20个字符之间'}), 400
        
        if not validate_email(email):
            return jsonify({'message': '邮箱格式不正确'}), 400
        
        is_valid_password, password_error = validate_password(password)
        if not is_valid_password:
            return jsonify({'message': password_error}), 400
        
        # 检查用户名和邮箱是否已存在
        if User.query.filter_by(username=username).first():
            return jsonify({'message': '用户名已存在'}), 409
        
        if User.query.filter_by(email=email).first():
            return jsonify({'message': '邮箱已被注册'}), 409
        
        # 创建新用户
        password_hash = AuthManager.hash_password(password)
        new_user = User(
            username=username,
            email=email,
            password_hash=password_hash,
            role='user'  # 默认角色为普通用户
        )
        
        db.session.add(new_user)
        db.session.commit()
        
        return jsonify({
            'message': '注册成功',
            'user': {
                'id': new_user.id,
                'username': new_user.username,
                'email': new_user.email,
                'role': new_user.role
            }
        }), 201
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'message': f'注册失败: {str(e)}'}), 500


@auth_bp.route('/login', methods=['POST'])
def login():
    """
    用户登录接口
    
    请求体参数:
        username (str): 用户名
        password (str): 密码
        
    返回:
        JSON: 登录结果，包含JWT令牌和用户信息
    """
    try:
        data = request.get_json()
        
        # 验证必需字段
        if not data or not all(k in data for k in ('username', 'password')):
            return jsonify({'message': '缺少用户名或密码'}), 400
        
        username = data['username'].strip()
        password = data['password']
        
        # 查找用户
        user = User.query.filter_by(username=username).first()
        if not user:
            return jsonify({'message': '用户名或密码错误'}), 401
        
        # 验证密码
        if not AuthManager.verify_password(user.password_hash, password):
            return jsonify({'message': '用户名或密码错误'}), 401
        
        # 生成JWT令牌
        token = AuthManager.generate_token(user.id, user.role)
        
        return jsonify({
            'message': '登录成功',
            'token': token,
            'user': {
                'id': user.id,
                'username': user.username,
                'email': user.email,
                'role': user.role,
                'created_at': user.created_at.isoformat()
            }
        }), 200
        
    except Exception as e:
        return jsonify({'message': f'登录失败: {str(e)}'}), 500


@auth_bp.route('/logout', methods=['POST'])
@token_required
def logout(current_user):
    """
    用户登出接口
    
    返回:
        JSON: 登出结果信息
    """
    # 由于使用JWT无状态设计，服务端不需要处理令牌失效
    # 客户端删除本地存储的令牌即可
    return jsonify({'message': '登出成功'}), 200


@auth_bp.route('/profile', methods=['GET'])
@token_required
def get_profile(current_user):
    """
    获取当前用户信息接口
    
    返回:
        JSON: 用户信息
    """
    return jsonify({
        'user': {
            'id': current_user.id,
            'username': current_user.username,
            'email': current_user.email,
            'role': current_user.role,
            'created_at': current_user.created_at.isoformat(),
            'avatar_url': current_user.avatar_url,
            'bio': current_user.bio
        }
    }), 200


@auth_bp.route('/profile', methods=['PUT'])
@token_required
def update_profile(current_user):
    """
    更新用户信息接口
    
    请求体参数（JSON 或 multipart/form-data）:
        username (str, 可选): 新用户名
        bio (str, 可选): 个人简介
        
    返回:
        JSON: 更新结果
    """
    try:
        data = request.get_json(silent=True) or {}
        
        # 允许空体（例如只更新头像的接口会复用此路由时）
        if not data and not request.files:
            return jsonify({'message': '没有提供更新数据'}), 400
        
        # 更新用户名
        if 'username' in data:
            new_username = data['username'].strip()
            
            if not new_username or len(new_username) < 3 or len(new_username) > 20:
                return jsonify({'message': '用户名长度必须在3-20个字符之间'}), 400
            
            # 检查用户名是否已被其他用户使用
            existing_user = User.query.filter_by(username=new_username).first()
            if existing_user and existing_user.id != current_user.id:
                return jsonify({'message': '用户名已存在'}), 409
            
            current_user.username = new_username
        
        # 更新个人简介
        if 'bio' in data:
            bio = str(data['bio'])
            # 可选：长度限制
            if len(bio) > 2000:
                return jsonify({'message': '个人简介长度不能超过2000字符'}), 400
            current_user.bio = bio
        
        db.session.commit()
        
        return jsonify({
            'message': '用户信息更新成功',
            'user': {
                'id': current_user.id,
                'username': current_user.username,
                'email': current_user.email,
                'role': current_user.role,
                'created_at': current_user.created_at.isoformat(),
                'avatar_url': current_user.avatar_url,
                'bio': current_user.bio
            }
        }), 200
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'message': f'更新失败: {str(e)}'}), 500


@auth_bp.route('/profile/avatar', methods=['PUT'])
@token_required
def upload_avatar(current_user):
    """
    上传并更新用户头像（支持普通用户和管理员）
    
    要求:
        - multipart/form-data，字段名: avatar
        - 仅支持 jpg/jpeg/png
        - 文件大小不超过 2MB
    
    返回:
        JSON: { message, avatar_url }
    """
    try:
        # 检查请求是否包含文件
        if 'avatar' not in request.files:
            return jsonify({'message': '未找到头像文件，请确保使用multipart/form-data格式上传'}), 400
        
        file = request.files['avatar']
        
        # 检查文件是否为空
        if not file or file.filename == '':
            return jsonify({'message': '未选择文件或文件为空'}), 400
        
        # 校验文件类型
        allowed_ext = {'jpg', 'jpeg', 'png'}
        filename = secure_filename(file.filename)
        if '.' not in filename:
            return jsonify({'message': '文件缺少扩展名，仅支持 jpg/jpeg/png 格式'}), 400
        
        ext = filename.rsplit('.', 1)[-1].lower()
        if ext not in allowed_ext:
            return jsonify({'message': f'不支持的文件格式: .{ext}，仅支持 jpg/jpeg/png 格式'}), 400
        
        # 校验文件大小（2MB）
        file.seek(0, os.SEEK_END)
        size = file.tell()
        file.seek(0)  # 重置文件指针到开头
        
        if size == 0:
            return jsonify({'message': '文件大小为0，请选择有效的图片文件'}), 400
        
        if size > 2 * 1024 * 1024:
            return jsonify({'message': f'图片大小不能超过 2MB，当前文件大小: {size / 1024 / 1024:.2f}MB'}), 400
        
        # 创建保存目录
        base_folder = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static', 'avatar')
        os.makedirs(base_folder, exist_ok=True)
        
        # 生成唯一文件名
        unique_name = f"{uuid.uuid4().hex}.{ext}"
        save_path = os.path.join(base_folder, unique_name)
        
        # 保存文件
        file.save(save_path)
        
        # 验证文件是否成功保存
        if not os.path.exists(save_path):
            return jsonify({'message': '文件保存失败，请重试'}), 500
        
        # 生成可访问URL
        relative_path = f"avatar/{unique_name}"
        avatar_url = url_for('static', filename=relative_path, _external=True)
        
        # 更新用户头像URL（支持普通用户和管理员）
        current_user.avatar_url = avatar_url
        db.session.commit()
        
        return jsonify({'message': '头像上传成功', 'avatar_url': avatar_url}), 200
        
    except Exception as e:
        db.session.rollback()
        current_app.logger.error(f'头像上传失败: {str(e)}', exc_info=True)
        return jsonify({'message': f'头像上传失败: {str(e)}'}), 500


@auth_bp.route('/change-password', methods=['PUT'])
@token_required
def change_password(current_user):
    """
    修改密码接口
    
    请求体参数:
        old_password (str): 原密码
        new_password (str): 新密码
        
    返回:
        JSON: 修改结果
    """
    try:
        data = request.get_json()
        
        if not data or not all(k in data for k in ('old_password', 'new_password')):
            return jsonify({'message': '缺少原密码或新密码'}), 400
        
        old_password = data['old_password']
        new_password = data['new_password']
        
        # 验证原密码
        if not AuthManager.verify_password(current_user.password_hash, old_password):
            return jsonify({'message': '原密码错误'}), 401
        
        # 验证新密码
        is_valid_password, password_error = validate_password(new_password)
        if not is_valid_password:
            return jsonify({'message': password_error}), 400
        
        # 更新密码
        current_user.password_hash = AuthManager.hash_password(new_password)
        db.session.commit()
        
        return jsonify({'message': '密码修改成功'}), 200
        
    except Exception as e:
        db.session.rollback()
        return jsonify({'message': f'密码修改失败: {str(e)}'}), 500
